package com.malty.mall.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.malty.mall.dao.ums.UserAdminMapper;
import com.malty.mall.domain.dto.req.UmsAdminParam;
import com.malty.mall.domain.dto.req.UmsRoleParam;
import com.malty.mall.domain.entity.ums.UmsResource;
import com.malty.mall.domain.entity.ums.UmsRoleResourceRelation;
import com.malty.mall.domain.entity.ums.UserAdmin;
import com.malty.mall.domain.entity.ums.UserAdminLoginLog;
import com.malty.mall.domain.model.AdminUserDetails;
import com.malty.mall.manager.RedisCacheService;
import com.malty.mall.service.ums.*;
import com.malty.mall.util.JwtTokenUtil;
import com.malty.mall.util.PreconditionUtils;
import com.malty.mall.util.RequestUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author malty
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class UserAdminServiceImpl extends ServiceImpl<UserAdminMapper, UserAdmin> implements IUserAdminService {
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private IUserAdminLoginLogService userAdminLoginLogService;
    @Autowired
    private RedisCacheService redisCacheService;
    @Autowired
    private IUmsAdminRoleService adminRoleService;
    @Autowired
    private IUmsRoleService roleService;
    @Autowired
    private IUmsResourceService resourceService;
    @Autowired
    private IUmsRoleResourceService roleResourceService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserAdmin userAdmin = this.getOne(Wrappers.<UserAdmin>lambdaQuery().eq(UserAdmin::getUsername, username));
        List<UmsRoleParam> roleParams = roleService.listByUid(userAdmin.getId());
        List<UmsResource> resourceList = getResourceList(roleParams);
        return new AdminUserDetails(userAdmin, resourceList);
    }

    @Override
    public UserAdmin register(UmsAdminParam umsAdminParam) {
        UserAdmin originUser = this.getOne(Wrappers.<UserAdmin>lambdaQuery().eq(UserAdmin::getUsername, umsAdminParam.getUsername()));
        if (originUser == null) {
            UserAdmin userAdmin = new UserAdmin(umsAdminParam);
            userAdmin.setStatus("1");
            // 密码 加密处理
            if (passwordEncoder != null) {
                String encodedPwd = passwordEncoder.encode(userAdmin.getPassword());
                userAdmin.setPassword(encodedPwd);
            }
            boolean save = this.save(userAdmin);
            if (!save) {
                throw new RuntimeException("注册失败");
            }
            return this.getOne(Wrappers.<UserAdmin>lambdaQuery().eq(UserAdmin::getUsername, userAdmin.getUsername()));
        }
        throw new RuntimeException("user registered!!");
    }

    @Override
    public String login(String username, String password) {
        String token = null;
        try {
            UserDetails userDetails = loadUserByUsername(username);
            if (passwordEncoder != null && !passwordEncoder.matches(password, userDetails.getPassword())) {
                throw new RuntimeException("密码错误");
            }
            if (!userDetails.isEnabled()) {
                throw new RuntimeException("用户不存在");
            }
            // UsernamePasswordAuthenticationToken 实现了 Authentication将用户信息和权限信息放在 Authentication中
            {
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
            token = jwtTokenUtil.generateToken(userDetails);
            insertLoginLog(username);
        } catch (Exception e) {
            log.warn("登录异常:{}", e.getMessage());
            return e.getMessage();
        }
        return token;
    }

    private void insertLoginLog(String username) {
        UserAdmin admin = getAdminByUsername(username);
        if (admin != null) {
            UserAdminLoginLog loginLog = new UserAdminLoginLog();
            loginLog.setUsername(admin.getId() + "");
            loginLog.setCreateTime(new Date());
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            assert attributes != null;
            HttpServletRequest request = attributes.getRequest();
            loginLog.setIp(RequestUtil.getRequestIp(request));
            userAdminLoginLogService.save(loginLog);
        }
    }

    @Override
    public UserAdmin getAdminByUsername(String username) {
        //先从缓存中获取数据
        UserAdmin admin = redisCacheService.getAdmin(username);
        if (admin == null) {
            //缓存中没有从数据库中获取
            admin = this.getOne(Wrappers.<UserAdmin>lambdaQuery().eq(UserAdmin::getUsername, username));
            if (admin != null) {
                //将数据库中的数据存入缓存中
                redisCacheService.setAdmin(admin);
                return admin;
            }
            return null;
        }
        return admin;
    }

    @Override
    public Boolean relateRole(Integer uid, Integer roleId) {
        UserAdmin byId = this.getById(uid);
        PreconditionUtils.check(byId == null, "用户不存在");
        return adminRoleService.createOrUpdate(uid, roleId);
    }

    private List<UmsResource> getResourceList(List<UmsRoleParam> roleParams) {
        Set<Integer> roleIds = roleParams.stream().map(UmsRoleParam::getId).collect(Collectors.toSet());
        if (!ObjectUtils.isEmpty(roleIds)) {
            List<UmsRoleResourceRelation> relations = roleResourceService.list(Wrappers.<UmsRoleResourceRelation>lambdaQuery().in(UmsRoleResourceRelation::getRoleId, roleIds));
            List<Integer> resourceIds = relations.stream().map(UmsRoleResourceRelation::getResourceId).collect(Collectors.toList());
            if (!ObjectUtils.isEmpty(resourceIds)) {
                List<UmsResource> resourceList = resourceService.list(Wrappers.<UmsResource>lambdaQuery().in(UmsResource::getId, resourceIds));
                return resourceList;
            }
        }
        return new ArrayList<>();
    }

    @Override
    public String refreshToken(String oldToken) {
        return jwtTokenUtil.refreshToken(oldToken);
    }
}
